home *** CD-ROM | disk | FTP | other *** search
Oberon Text | 1990-11-07 | 27.9 KB | 516 lines | [.Ob./.Ob*] |
- Syntax16.Scn.Fnt
- Syntax10.Scn.Fnt
- Syntax14.Scn.Fnt
- Syntax12i.Scn.Fnt
- Syntax12.Scn.Fnt
- Syntax12b.Scn.Fnt
- Guide for Programmers of new Frame Classes and Viewer Types
- Frames as Active Objects
- Frames are the basic entities of Oberon's display system. They are more-or-less autonomous rectangular
- areas on the display screen and may be nested. In particular, the display screen is hierarchically organized
- like this: Display > tracks > viewers > data frames. Display, tracks, and viewers are entities to be managed by
- the viewer handler module Viewers. Because of Oberon's tiling approach, all of these frames are totally
- visible or totally invisible, i.e. there is no partial overlapping on this level. There exists a class of standard
- viewers called menu-viewers (and handled by module MenuViewers). Every menu-viewer contains exactly
- two vertically adjacent data frames: A header frame (including title and menu) and a main data frame. The
- main frame of a menu-viewer can be regarded as a virtual display terminal representing the user interface
- to a certain application or task. It may itself be nested, i.e. contain further subframes. Because an individual
- command interpreter is associated with every frame, implementing a new class of data frames is a most
- powerful way of adding functionality to the Oberon system.
- We have intentionally used the term class. As a matter of fact, frames are implemented in Oberon as active
- objects in the sense of object-oriented programming. Calls of frame-specific handling procedures are made
- by system routines in the form of message transfers. In particular, the central Oberon loop typically initiates
- reactions on user-input actions by sending appropriate messages to the respective viewers. We emphasize
- that employing the object-oriented programming paradigm in connection with the handling of frames is
- necessary in order to enable the kernel to call command interpreters whose identity is unknown to it.
- Any handler that is associated with a certain viewer class, for example menu-viewers, is expected to handle
- messages from (at least) three different originators: From the viewer manager, from the document
- manager, and from the central loop. Messages from the viewer manager report on the state change of the
- viewer. For example, the viewer manager Viewers sends a message whenever a hidden viewer becomes
- visible (and must therefore restore its contents), or when the size of a viewer has changed because its lower
- neighbour was opened, changed, or closed. Messages from the document manager remind the viewer of
- updating its contents after an editing operation. Finally, messages from module Oberon notify the viewer of
- system-wide events, such as input events (for example, "mouse button i pressed at position X, Y") or an
- inquiry for the most recent text selection (regarded in Oberon as the standard input). The interpreter-part
- handling input events should be regarded as an editor that is bound to the viewer class. In the case of
- viewers displaying text, this is a text editor, in the case of graphics, it is a graphics editor, and in the case of a
- viewer representing a mailbox it is an editor for electronic mail.
- A typical viewer handler (like the one associated with menu-viewers) processes viewer-oriented messages
- directly and delegates the handling of the more data-specific messages by passing them on to the viewer's
- subframes. Prime examples of viewer-oriented messages are requests to restore a viewer or change its size.
- Examples of data-specific messages are selecting an object or invoking a command by pointing to its name.
- Notice that new and finer-grained requests may evolve from processing of viewer-oriented message.s For
- example, a request to change the size of a menu-viewer is resolved in requests to change the size of one or
- both of its subframes. In summarizing, viewer handlers normally act both as mediators and originators of
- messages to be processed by the handlers of their subframes.
- The Canonical Decomposition of an Application
- Modularizing by separation of concerns is one of the most effective techniques applied to the design of large
- software systems. Our concept of a class of frames displaying data of a certain kind provides a perfect
- opportunity to demonstrate this technique. We can extract the following separate topics from the program
- implementing an interactive application: the tool package, the command interpreter, the display handler, and
- the data manager. The following tasks are assigned to these parts: Providing a collection of powerful and
- customized commands operating on the data of the given kind (tool package), reacting on input actions
- within the associated display frame (command interpreter), displaying the visible objects (display handler),
- and encapsulating the management of the data without any concern of how they are displayed (data
- manager). Typically, the data part comprises a set of editing operations. It maintains an internal data
- structure describing the current state of the data.
- It is natural to try to assign a separate module to each of these parts. We soon see that the command
- interpreter part and the display manager are preferably combined in one module because both need to
- operate on the same associated display frame. This leads to the following canonical module configurations
- in the casess of standard texts, graphics, pictures, and MyData, for example:
- Tool Package Edit Draw Paint MyTool
- Comm. Int. & Disp. Handler TextFrames GraphicFrames PictureFrames MyFrames
- Data Manager Texts Graphics Pictures MyData
- Notice that the same data manager can in principle be used by different display handlers and command
- interpreters. Data managers serve as links between different applications and thus enable an optimal
- integration. For example, if the graphic system is based on module Texts for the management of text
- captions, then text can freely be exchanged between frames of these classes: Graphic frames and text frames
- are integrated.
- Tutorial Example: Text Viewers
- So far, we have not explained yet how objects and messages are realized in the Oberon language. In contrast
- to typical object-oriented programming systems, the complete set of messages understood by an object
- need not be specified together with the definition of the object class. Instead, Oberon explores a more
- flexible dual approach: Messages are typically defined in sender modules. For example, messages of the first
- of the above mentioned kinds are defined in module Viewers, messages of the second kind are defined in
- the respective editor module (TextFrames in the case of text), and messages of the third kind are defined in
- module Oberon which detects input events.
- Roughly speaking, the Oberon language supports subclassing (by the type extension facility), but messages
- and message handlers are not institutionalized in the form of a language construct. We have implemented
- the above outlined scheme by making use of procedure variables, and by applying Oberon's record
- extension facility to objects and to messages.
- We shall now exemplify this model with the help of standard text-viewers, i.e. menu-viewers whose
- subframes (menu and main) are text frames. The following exposition may serve as a tutorial and template
- for implementors of arbitrary frame classes or viewer types. We recall that the display data structure is a
- hierarchy of display frames. Restricting our explanations to text-viewers we have the following hierarchy of
- types:
- Viewers.Track MenuViewers.Viewer
- ^ ^
- Viewers.Viewer TextFrames.Frame
- ^ ^
- Display.Frame
- Module Display features the base types of frames and frame messages and the type of the message handler.
- The (empty) base message serves as a root in the (potentially unlimited) hierarchy of messages to be
- accepted by frames. Module Viewers provides the definition of type Viewer, which is an extension (variant)
- of type Display.Frame. Menu-viewers are a based on Viewers.Viewer an defined in module MenuViewers.
- In definition of Display:
- TYPE
- Frame = POINTER TO FrameDesc;
- FrameMsg = RECORD END;
- Handler = PROCEDURE (Frame, VAR FrameMsg);
- FrameDesc = RECORD
- dsc, next: Frame; (*son, brother*)
- X, Y, W, H: INTEGER;
- handle: Handler
- END;
- In the definition of Viewers:
- TYPE
- Viewer = POINTER TO ViewerDesc;
- ViewerDesc = RECORD (Display.FrameDesc)
- state: INTEGER
- END;
- In the definition of MenuViewers:
- TYPE
- Viewer = POINTER TO ViewerDesc;
- ViewerDesc = RECORD (Viewers.ViewerDesc)
- menuH: INTEGER
- END;
- The following are the declarations of messages that are expected to be handled by every viewer. Note that
- they are distributed over several modules rather than concentrated in one place (as it would be the case in
- an "ordinary" object-oriented model).
- In definition of Viewers:
- ViewerMsg = RECORD (Display.FrameMsg)
- id: INTEGER;
- X, Y, W, H: INTEGER; (*new rectangle*)
- state: INTEGER (*new state*)
- END;
- (*signals change of viewer state
- id = 0: restore viewer
- 1: modify size at bottom
- 2: suspend viewer*)
- In definition of TextFrames:
- UpdateMsg = RECORD (Display.FrameMsg)
- id: INTEGER; (*operation*)
- text: Texts.Text; (*edited text*)
- beg, end: LONGINT (*stretch*)
- END;
- (*signals change of contents
- id = 0: stretch [beg, end[ replaced
- 1: stretch [beg, end[ inserted
- 2: stretch [beg, end[ deleted*)
- In definition of Oberon:
- InputMsg = RECORD (Display.FrameMsg)
- id: INTEGER; (*operation*)
- modes, keys: SET; (*mouse*)
- X, Y: INTEGER; (*position*)
- ch: CHAR (*character read*)
- END;
- (*signals input event
- id = 0: track mouse
- 1: consume character*)
- ControlMsg = RECORD (Display.FrameMsg)
- id: INTEGER; (*operation*)
- X, Y: INTEGER (*current location of the mous*)
- END;
- (*signals control action
- id = 0: remove focus
- 1: remove all marks
- 2: : mark*)
- SelectionMsg = RECORD (Display.FrameMsg)
- time: LONGINT;
- text: Texts.Text;
- beg, end: LONGINT
- END;
- (*signals inquiry for most recent text selection*)
- CopyOverMsg = RECORD (Display.FrameMsg)
- text: Texts.Text;
- beg, end: LONGINT
- END;
- (*receive text stretch [beg, end[*)
- CopyMsg* = RECORD (Display.FrameMsg)
- F: Display.Frame
- END;
- (*request for the creation of a copy of a text frame*)
- We recall that high-level viewer managers like MenuViewers typically pass on most of the received messages
- to their subframes. In the course of (pre-)processing viewer-oriented requests, high-level viewer managers
- can also produce new messages. In the case of MenuViewers these are
- ModifyMsg* = RECORD (Display.FrameMsg)
- id: INTEGER; (*operation*)
- dY, Y, H: INTEGER (*vector dY, new Y and H*)
- END;
- (*vertically move and extend or reduce frame at bottom*)
- id = 0: extend frame
- id = 1: reduce frame*)
- dY represents a vertical translation vector showing upwards in the extend-case and showing downwards in
- the reduce-case. By definition, dY is always non-negative. Y and H specify the new position and height
- respectively.
- In summary, essentially the same messages have to be handled by a viewer and its subframes, except that
- some of the messages are processed or preprocessed by the ancestor viewer already which, in turn, may
- result in sending new and finer-grained messages to its subframes.
- A message is sent to a specific object simply by calling its installed handler. For example, F.handle(F, M) has
- the effect of sending message M to frame F. In case of text-frames, the installed handler is Handle. It is
- elaborated in module TextFrames. We now provide a tutorial sketch of this procedure. Notice that Handle
- makes extensive use of Oberon's type test (and type guard) facility in order to discriminate among the
- different message kinds.
- 1 PROCEDURE Handle (F: Display.Frame; VAR M: Display.FrameMsg);
- 2 VAR F1: Frame;
- 3 BEGIN
- 4 WITH F: Frame DO
- 5 IF M IS Oberon.InputMsg THEN
- 6 WITH M: Oberon.InputMsg DO
- 7 IF M.id = Oberon.track THEN Edit(F, M.X, M.Y, M.keys)
- 8 ELSIF M.id = Oberon.consume THEN
- 9 IF F.car # 0 THEN Write(F, M.ch, M.fnt, M.col, M.voff) END
- 12 END
- 13 END
- 14 ELSIF M IS Oberon.ControlMsg THEN
- 15 WITH M: Oberon.ControlMsg DO
- 16 IF M.id = Oberon.defocus THEN Defocus(F)
- 17 ELSIF M.id = Oberon.neutralize THEN Neutralize(F)
- 18 END
- 19 END
- 20 ELSIF M IS Oberon.SelectionMsg THEN
- 21 WITH M: Oberon.SelectionMsg DO GetSelection(F, M.text, M.beg, M.end, M.time) END
- 22 ELSIF M IS Oberon.CopyOverMsg THEN
- 23 WITH M: Oberon.CopyOverMsg DO CopyOver(F, M.text, M.beg, M.end) END
- 24 ELSIF M IS Oberon.CopyMsg THEN
- 25 WITH M: Oberon.CopyMsg DO Copy(F, F1); M.F := F1 END
- 26 ELSIF M IS MenuViewers.ModifyMsg THEN
- 27 WITH M: MenuViewers.ModifyMsg DO Modify(F, M.id, M.dY, M.Y, M.H) END
- 28 ELSIF M IS UpdateMsg THEN
- 29 WITH M: UpdateMsg DO
- 30 IF F.text = M.text THEN Update(F, M) END
- 31 END
- 32 END
- 33 END
- 34 END Handle;
- Explanations:
- 1 procedure of type Display.Handler
- 2 auxiliary variable for frame copy (line 25). Needed because M.F is base type
- 4 type guard; F must be a text frame
- 5 type test; is message an Oberon input message?
- 6 type guard
- 7 if message demands mouse tracking
- 8 if message demands consuming then consume a character
- 9 if caret is active in this text frame
- 14 type test; is message an Oberon control message?
- 15 type guard
- 16 if message demands removing the caret
- 17 if message demands removing caret and selection
- 20 type test; is message an Oberon selection inquiry?
- 21 if so, register own selection if it is newer than candidate
- 22 type test; is message a copy-over-message?
- 23 if so, copy text stretch to caret's location in this frame
- 24 type test; is message a copy-message?
- 25 if so, produce a copy of this frame (initialized to empty)
- 26 type test; is message a modify-message?
- 27 if so, modify size or position of this frame (see below)
- 28 type test; is message an update message?
- 30 if so, check if changed text is represented in this frame and then update frame
- We also provide the following refinement of the procedure TextFrames.Modify called in line 27 in
- TextFrames.Handle. It shows well how subframes of menu-viewers should react on a
- MenuViewers.ModifyMsg.
- PROCEDURE Modify (F: Frame; id, dY, Y, H: INTEGER);
- BEGIN
- Mark(F, 0); RemoveMarks(F); (*remove position-bar, caret, and selection*)
- IF id = MenuViewers.extend THEN
- IF dY > 0 THEN (*if frame is to be moved*)
- Display.CopyBlock(F.X, F.Y, F.W, F.H, F.X, F.Y + dY, 0); F.Y := F.Y + dY (*move original block*)
- END;
- Extend(F, Y) (*extend moved frame at its bottom*)
- ELSIF id = MenuViewers.reduce THEN
- Reduce(F, Y + dY); (*reduce original frame at its bottom*)
- IF dY > 0 THEN (*if frame is to be moved*)
- Display.CopyBlock(F.X, F.Y, F.W, F.H, F.X, Y, 0); F.Y := Y (*move reduced block*)
- END
- END;
- IF F.H > 0 THEN Mark(F, 1) END (*restore position-bar*)
- END Modify;
- Remember that dY is always non-negative, and notice that the reduce-part of the procedure is the exact
- inverse of the extend-part.
- Notice by the way that the above handler is used for the handling of both data-frames and standard
- header-frames. The two variants of text frames differ only by their background color. This fact testifies for a
- streamlined system design and is an example of the "today en-vogue" notion of code reusing.
- The attentative and experienced reader may have noticed that the module TextFrames plays two very
- different roles. Its first role is that of a (late-bound) handler of messages sent to frame instances. The second
- role is that of a library of procedures operating on text frames. Examples are Edit, Write, CopyOverShow,
- SetCaret, and TrackWord. To the two roles of TextFrames correspond two different ways of treating text
- frames, namely as active objects individually reacting on messages, and as passive rectangles to be operated on
- conventionally by invoking procedures. The second way of treating frames might be of importance in cases
- where text frames are text boxes belonging to the contents of some more complex document.
- Module TextFrames offers yet another choice, this time to potential implementors of handlers of subclasses
- of text frames: Either they implement their own handler by just adding increments, and then refer to Handle,
- or they compose an individual handler from the elements. For example, a designer of MailFrames might
- apply the first strategy. He might just add a few mail-oriented methods catching the mail-oriented
- messages, and then delegate all further processing to TextFrames.Handle. In contrast, an implementor of a
- new user-interface to text frames might prefer strategy 2.
- We conclude this section by explaining briefly the flow of control after, for example, a character has been
- typed. We assume that the focus viewer is a text viewer and that the caret is set in its main data frame. First,
- the central loop Oberon detects the new character in the keyboard buffer. It then sends an input
- consume-message to the focus-viewer which, in turn, passes on this message to its main subframe. After
- that, the handler (command interpreter) of this subframe locates the caret within the underlying text and
- subsequently calls the Insert-procedure of the text data manager Texts. On that, Insert inserts the character
- in the text and (up-)calls the associated notifier which, in our case, is residing in TextFrames. The notifier
- then sends a broadcast-message of type TextFrames.UpdateMsg to all visible viewers. Every single of these
- viewers passes on this message to its subframes. If a subframe understands the message and finds itself
- involved in this update, it restores its contents by accessing the new data, again from the data manager Texts.
- We now present some typical examples of implementations.
- Tutorial Examples 1: Frame oriented operations
- Display text line within text frame
- PROCEDURE DisplayLine (F: Frame; L: Line; VAR R: Texts.Reader; X, Y: INTEGER; len: LONGINT);
- VAR pat: Display.Pattern; NX, dx, x, y, w, h: INTEGER;
- BEGIN NX := F.X + F.W;
- WHILE (nextCh # CarriageReturn) & (R.fnt # NIL) DO
- Display.GetChar(R.fnt.raster, nextCh, dx, x, y, w, h, pat);
- IF (X + x + w <= NX) & (h # 0) THEN
- Display.CopyPattern(R.col, pat, X + x, Y + y, 2)
- END;
- X := X + dx; INC(len); Texts.Read(R, nextCh)
- END;
- L.len := len + 1; L.wid := X + eolW - (F.X + F.margW);
- L.eot := R.fnt = NIL; Texts.Read(R, nextCh)
- END DisplayLine;
- where the types Line and Frame are defined as follows. Notice that Line is a private type and TextFrames.Frame
- is the public projection of type Frame.
- Line = POINTER TO LineDesc;
- LineDesc = RECORD
- len: LONGINT; (*number of characters in this line*)
- wid: INTEGER; (*width of line box*)
- eot: BOOLEAN; (*end of text flag*)
- next: Line (*next lower neighbour*)
- END;
- Location = RECORD
- org, pos: LONGINT; (*line origin, position*)
- dx, x, y: INTEGER; (*width and position of located character*)
- lin: Line (*associated line*)
- END;
- Frame = POINTER TO FrameDesc;
- FrameDesc = RECORD (Display.FrameDesc)
- text: Texts.Text; (*displayed text*)
- org: LONGINT; (*position in text of first displayed character*)
- col: INTEGER; (*background color*)
- lsp, asr, dsr: INTEGER; (*line spacing, ascender, descender*)
- left, right, top, bot: INTEGER; (*margins*)
- markH: INTEGER; (*margin width, position of mark*)
- time: LONGINT; (*time of latest selection*)
- mark, car, sel: INTEGER; (*state of mark, caret, selection*)
- carloc: Location; (*caret location*)
- selbeg, selend: Location (*locations of begin and end of selection*)
- trailer: Line (*pointer to the associated sequence of line descriptors*)
- END;
- Track caret
- PROCEDURE TrackCaret (F: Frame; X, Y: INTEGER; VAR keysum: SET);
- VAR loc: Location; keys: SET;
- BEGIN
- IF F.trailer.next # F.trailer THEN
- LocateChar(F, X - F.X, Y - F.Y, F.carloc);
- FlipCaret(F);
- keysum := {};
- LOOP
- Input.Mouse(keys, X, Y);
- IF keys = {} THEN EXIT END;
- keysum := keysum + keys;
- Oberon.DrawCursor(Oberon.Mouse, Oberon.Mouse.marker, X, Y);
- LocateChar(F, X - F.X, Y - F.Y, loc);
- IF loc.pos # F.carloc.pos THEN FlipCaret(F); F.carloc := loc; FlipCaret(F) END
- END;
- F.car := 1
- END
- END TrackCaret;
- where the following auxiliary procedures are used:
- PROCEDURE LocateChar (F: Frame; x, y: INTEGER; VAR loc: Location);
- VAR R: Texts.Reader;
- pat: Display.Pattern;
- pos, lim: LONGINT;
- ox, dx, u, v, w, h: INTEGER;
- BEGIN LocateLine(F, y, loc);
- lim := loc.org + loc.lin.len - 1;
- pos := loc.org; ox := F.left;
- Texts.OpenReader(R, F.text, loc.org); Texts.Read(R, nextCh);
- LOOP
- IF pos = lim THEN dx := eolW; EXIT END;
- Display.GetChar(R.fnt.raster, nextCh, dx, u, v, w, h, pat);
- IF ox + dx > x THEN EXIT END;
- INC(pos); ox := ox + dx; Texts.Read(R, nextCh)
- END;
- loc.pos := pos; loc.dx := dx; loc.x := ox
- END LocateChar;
- PROCEDURE LocateLine (F: Frame; y: INTEGER; VAR loc: Location);
- VAR T: Texts.Text; L: Line; org: LONGINT; cury: INTEGER;
- BEGIN T := F.text;
- org := F.org; L := F.trailer.next; cury := F.H - F.top - F.asr;
- WHILE (L.next # F.trailer) & (cury > y + F.dsr) DO
- org := org + L.len; L := L.next; cury := cury - F.lsp
- END;
- loc.org := org; loc.lin := L; loc.y := cury
- END LocateLine;
- PROCEDURE FlipCaret (F: Frame);
- BEGIN
- IF F.carloc.x < F.W THEN
- IF (F.carloc.y >= 10) & (F.carloc.x + 12 < F.W) THEN
- Display.CopyPattern(white, BigCaret, F.X + F.carloc.x, F.Y + F.carloc.y - 10, 2)
- ELSIF (F.carloc.y >= 4) & (F.carloc.x + 6 < F.W) THEN
- Display.CopyPattern(white, SmallCaret, F.X + F.carloc.x, F.Y + F.carloc.y - 4, 2)
- END
- END
- END FlipCaret;
- Tutorial Examples 2: Text oriented operations
- Save text in buffer
- PROCEDURE Save (T: Text; beg, end: LONGINT; B: Buffer);
- VAR p, q, qb, qe: Piece; org: LONGINT;
- BEGIN
- IF end > T.len THEN end := T.len END;
- FindPiece(T, beg, org, p);
- NEW(qb); qb^ := p^;
- qb.len := qb.len - (beg - org);
- qb.off := qb.off + (beg - org);
- qe := qb;
- WHILE end > org + p.len DO
- org := org + p.len; p := p.next;
- NEW(q); q^ := p^; qe.next := q; q.prev := qe; qe := q
- END;
- qe.next := NIL; qe.len := qe.len - (org + p.len - end);
- B.last.next := qb; qb.prev := B.last; B.last := qe;
- B.len := B.len + (end - beg)
- END Save;
- where FindPiece is the following auxiliary procedure:
- PROCEDURE FindPiece (T: Text; pos: LONGINT; VAR org: LONGINT; VAR p: Piece);
- VAR n: INTEGER;
- BEGIN
- IF pos < T.org THEN T.org := -1; T.pce := T.trailer END;
- org := T.org; p := T.pce; (*from cache*)
- n := 0;
- WHILE pos >= org + p.len DO org := org + p.len; p := p.next; INC(n) END;
- IF n > 50 THEN T.org := org; T.pce := p END (*to cache*)
- END FindPiece;
- and where the types Piece, Text, and Buffer are defined as follows. Notice that Piece is a private type, Texts.Text
- is the public projection of type Text, and Texts.Buffer is the public part of type Buffer.
- Piece = POINTER TO PieceDesc;
- PieceDesc = RECORD
- f: Files.File; (*carrier file*)
- off: LONGINT; (*offset in file*)
- len: LONGINT; (*piece length*)
- fnt: Fonts.Font; (*font*)
- col: SHORTINT; (*color*)
- voff: SHORTINT; (*vertical offset*)
- prev, next: Piece (*links to neighbours*)
- END;
- Text = POINTER TO TextDesc;
- Notifier = PROCEDURE (Text, INTEGER, LONGINT, LONGINT);
- TextDesc = RECORD
- len: LONGINT; (*text length*)
- notify: Notifier; (*called after text changes*)
- trailer: Piece; (*sentinel*)
- org: LONGINT; (*cached origin*)
- pce: Piece (*cached piece*)
- END;
- Buffer = POINTER TO BufDesc;
- BufDesc = RECORD
- len: LONGINT; (*buffer length*)
- header, last: Piece (*buffered piece list*)
- END;
- Insert contents of buffer in text
- PROCEDURE Insert (T: Text; pos: LONGINT; B: Buffer);
- VAR pl, pr, p, qb, qe: Piece; org: LONGINT;
- BEGIN
- FindPiece(T, pos, org, p); SplitPiece(p, pos - org, pr);
- IF T.org >= org THEN (*adjust cache*)
- T.org := org - p.prev.len; T.pce := p.prev
- END;
- pl := pr.prev; qb := B.header.next;
- IF (qb # NIL) & (qb.f = pl.f) & (qb.off = pl.off + pl.len)
- & (qb.fnt = pl.fnt) THEN pl.len := pl.len + qb.len; qb := qb.next
- END;
- IF qb # NIL THEN qe := B.last;
- qb.prev := pl; pl.next := qb; qe.next := pr; pr.prev := qe
- END;
- T.len := T.len + B.len;
- T.notify(T, insert, pos, pos + B.len); (*call postprocessor*)
- B.last := B.header; B.last.next := NIL; B.len := 0
- END Insert;
- where SplitPiece is
- PROCEDURE SplitPiece (p: Piece; off: LONGINT; VAR pr: Piece);
- VAR q: Piece;
- BEGIN
- IF off > 0 THEN NEW(q);
- q.col := p.col; q.fnt := p.fnt;
- q.len := p.len - off;
- q.f := p.f; q.off := p.off + off;
- p.len := off;
- q.next := p.next; p.next := q;
- q.prev := p; q.next.prev := q;
- pr := q
- ELSE pr := p
- END
- END SplitPiece;
- Literature
- Ceres Workstation
- H. Eberle. Development and Analysis of a Workstation Computer.
- Diss. ETH No. 8431, 1987.
- B. Heeb. Design of the Processor-Board for the Ceres-2 Workstation,
- Bericht 93, Inst. f
- r Informatik, ETH Z
- rich, November 1988.
- Oberon Language
- N. Wirth. Type Extensions.
- ACM Trans. on Prog. Languages and Systems, 10, 2 (April 1988), 204-214.
- N. Wirth. From Modula to Oberon.
- Software - Practice and Experience, 18, 7, (July 1988), 661-670.
- N. Wirth. The Programming Language Oberon.
- Software - Practice and Experience, 18, 7, (July 1988),
- 671- 690.
- J. Gutknecht. Variations on the Role of Module Interfaces.
- Structured Programming, 10, 1, (Jan. 1989), 40-46.
- Oberon System
- N. Wirth. An extensible system and a programming tool for workstation computers.
- Proc. IVth South African Computer Science Symposium. Pretoria, South Africa.
- N. Wirth. Oberon: An Extensible Operating System for Workstations.
- Proc. Euromicro Conf., Z
- rich, 29.8. - 1.9.1988.
- N. Wirth and J. Gutknecht. The Oberon System.
- Bericht 88, Inst. f
- r Informatik, ETH Z
- rich, July 1988,
- and Software - Practice and Experience, 19 (1989).
- N. Wirth. Designing a System from Scratch.
- Structured Programming, 10, 1 (Jan. 1989), 10-18.
-